﻿using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using TTGame;

/*
 * PuppetState
 * Puppet的状态机各个状态逻辑
 *
 * 将Puppet的状态逻辑代码拎出来。仅仅是为了减少Puppet类的复杂度。
 * 所以，PuppetState是专为Puppet使用，Puppet继承类型可以选择性的复用PuppetState的某状态逻辑，或者单独对某一个状态逻辑复写。
 *
 * Crossfade方案：
 * X 1.状态判断时间，提前零点几秒结束。
 * X 2.找到后面的动画，提前播放。（后面切换状态后，判断相同的动画就不切了。）
 * 3.ClampForever,播放结束后保持最后一帧的动作，并且动画处于播放中状态，保证可以正常Fade切换。
 *
 * mind:
 * 给状态加一个锁定时间，进入后锁定时间结束，才可切换状态。
 *
 * 依赖：
 * puppet,ObjectManager
 *
 * ！不写重复代码！
 * @author TTun
 * @date 7/12/2017 9:15:12 PM
 */
namespace TTGameEngine {

public class PuppetStandState : IFSMState {

    Puppet m_puppet;
    public string Name { get; set; }
    public int StateID { get; set; }

    public PuppetStandState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }

    public void OnEnter() {
        //一切换状态就设置动画，防止状态被顶替。如Run->Stand->Skill中，stand状态在update之前就被顶替。
        playStandAnim();
    }

    public IEnumerator updateState() {
        float dt = Time.deltaTime;
        bool isOnGuard = m_puppet.IsOnGuardState();

        while(m_puppet.StateID == PuppetStateID.Stand) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            bool newOnGuardState = m_puppet.IsOnGuardState();
            if(isOnGuard != newOnGuardState) {
                isOnGuard = newOnGuardState;
                playStandAnim();
            }

            if(m_puppet.IsMoving()) {
                m_puppet.SetState(PuppetStateID.Run);
            }

            //updateForceMove();

            if(m_puppet.StateID == PuppetStateID.Stand) {
                yield return null;
            }
        }
    }

    void playStandAnim() {
        m_puppet.m_animPlayer.PlayAnimWithFade(m_puppet.GetStandAnim(), 0.1f);
    }

    void updateForceMove() {
        float dt = Time.deltaTime;
        m_puppet.PuppetMoveInfo.moveSpeed = m_puppet.MoveSpeed;
        Vector3 nextPos = m_puppet.objTF.position;
        //强制位移
        Tools.ObjForceStep(dt, m_puppet.objTF.position, m_puppet.PuppetForceMoveInfos.Values, out nextPos);
        //m_puppet.objTF.position = nextPos;
        m_puppet.SimpleMove(nextPos);
    }

    public bool IsCanChangeState(int state) {
        return true;
    }
}


public class PuppetShowState : IFSMState {

    public string Name { get; set; }
    public int StateID { get; set; }
    Puppet m_puppet;

    public PuppetShowState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }
    public void OnEnter() {
        m_puppet.m_animPlayer.PlayAnimWithFade(m_puppet.showAnimName);
    }

    public IEnumerator updateState() {

        while(m_puppet.StateID == PuppetStateID.Show) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            if(m_puppet.IsMoving()) {
                m_puppet.SetState(PuppetStateID.Run);
            }

            if(m_puppet.StateID == PuppetStateID.Show) {
                yield return null;
            }
        }
    }

    public bool IsCanChangeState(int state) {
        return true;
    }
}

public class PuppetRunState : IFSMState {

    public string Name { get; set; }
    public int StateID { get; set; }
    Puppet m_puppet;

    AnimationState runAnim;

    public PuppetRunState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }
    public void OnEnter() {
        m_puppet.m_animPlayer.PlayAnimWithFade(m_puppet.runAnimName, 0.3f);
        runAnim = m_puppet.m_animPlayer.GetAnimation(m_puppet.runAnimName);
    }
    public IEnumerator updateState() {
        while(m_puppet.StateID == PuppetStateID.Run) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            OnStateUpdate();

            if(m_puppet.StateID == PuppetStateID.Run) {
                yield return null;
            }
        }
    }

    /// <summary>
    /// 单独拿出来，方便debug。协程内不方便看变量。
    ///
    /// mark:youhua 移动排斥可以减小刷新频率
    /// </summary>
    void OnStateUpdate() {
        float dt = Time.deltaTime;
        if(m_puppet.IsMoving()) {
            Vector3 colliderMove = Vector3.zero;
            List<Puppet> objs = ObjectManager.Share().GetCampObjList(m_puppet.CampID);
            for(int i = 0; i < objs.Count; i++) {
                Puppet aroundObj = objs[i];
                if(aroundObj != m_puppet && aroundObj.StateID != PuppetStateID.Run) {
                    Vector3 colliderVectory = m_puppet.objTF.position - aroundObj.objTF.position;
                    float distSqr = colliderVectory.sqrMagnitude;
                    float ColliderRange = (aroundObj.moveColliderRange + m_puppet.moveColliderRange);
                    if(distSqr < ColliderRange * ColliderRange) {
                        colliderMove += colliderVectory.normalized * (ColliderRange * ColliderRange - distSqr);
                    }
                }
            }
            if(DebugSetting.Self.IsColliderMove && colliderMove != Vector3.zero) {
                colliderMove = colliderMove.normalized;
                m_puppet.PuppetMoveInfo.colliderOffsetDir = colliderMove * DebugSetting.Self.ColliderMoveFactor;
            } else {
                m_puppet.PuppetMoveInfo.colliderOffsetDir = Vector3.zero;
            }

            updateRunAudio();
            updateMove();

            m_puppet.LookAtDir(m_puppet.CurMoveDir);


        } else {
            if(m_puppet.StateID == PuppetStateID.Run) {
                m_puppet.SetState(PuppetStateID.Stand);
            }
        }
    }

    void updateMove() {
        float dt = Time.deltaTime;
        m_puppet.PuppetMoveInfo.moveSpeed = m_puppet.MoveSpeed;
        Vector3 nextPos = m_puppet.objTF.position;
        //强制位移
        //Tools.ObjForceStep(dt, m_puppet.objTF.position, m_puppet.PuppetForceMoveInfos.Values, out nextPos);
        //正常位移
        bool isMoveEnd = m_puppet.PuppetMoveInfo.ObjMoveStep(dt, nextPos, out nextPos);
        //m_puppet.CurMoveDir = nextPos - m_puppet.transform.position;
        m_puppet.SimpleMove(nextPos);
        if(isMoveEnd) {
            //移动结束
            m_puppet.StopMove();
        }
    }

    bool footAudioOn1 = false;
    bool footAudioOn2 = false;
    string curFootAudioName;

    void updateRunAudio() {
        if(runAnim == null) {
            return;
        }

        //循环播放normalizedTime会超过1.
        float progress = runAnim.normalizedTime - (int)runAnim.normalizedTime;
        if(progress > m_puppet.footAnimPoint1) {
            if(footAudioOn1 == false) {
                curFootAudioName = m_puppet.footstepSoundNames.RandomValue();
                AudioExtensions.PlayClip(curFootAudioName, m_puppet.Pos);
                footAudioOn1 = true;
            } else if(progress > m_puppet.footAnimPoint2 && footAudioOn2 == false) {
                AudioExtensions.PlayClip(curFootAudioName, m_puppet.Pos);
                footAudioOn2 = true;
            }
        } else {
            footAudioOn1 = false;
            footAudioOn2 = false;
        }
    }

    public bool IsCanChangeState(int state) {
        return true;
    }
}

public class PuppetHurtState : IFSMState {

    public string Name { get; set; }
    public int StateID { get; set; }
    Puppet m_puppet;

    float stateTime = 0f;      //状态时间结束就会退出状态
    float limitPlayTime = 0f;  //至少要播放完一定时间才可切换状态
    string CurHurtAnimName;

    public PuppetHurtState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }

    public void OnEnter() {
        CurHurtAnimName = m_puppet.GetHurtAnim();
        float animTime = m_puppet.m_animPlayer.GetAnimLength(CurHurtAnimName);
        //提前结束，动画留一点时间用来做动画过渡。
        stateTime = animTime * Puppet.StateEndingTimeFactor;
        limitPlayTime = animTime * m_puppet.StateLimitTimeFactor;
        m_puppet.PuppetLog("limitTime:[{0}]", limitPlayTime);
        m_puppet.m_animPlayer.PlayAnimWithBlenad(CurHurtAnimName);
        playHurtAuido();
    }

    public IEnumerator updateState() {
        while(m_puppet.StateID == PuppetStateID.Hurt) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            float dt = Time.deltaTime;
            if(stateTime < 0) {
                if(m_puppet.curDamageInfo.hurtType == HurtType.KnockDown) {
                    m_puppet.SetState(PuppetStateID.ClimbUp);
                } else {
                    m_puppet.SetState(PuppetStateID.Stand);
                }
                break;
            }
            stateTime -= dt;
            limitPlayTime -= dt;

            if(m_puppet.StateID == PuppetStateID.Hurt) {
                yield return null;
            }
        }
    }

    void playHurtAuido() {
        HurtType hurtType = m_puppet.GetHurtType();
        switch(hurtType) {
            case HurtType.LightHurt:
                AudioExtensions.PlayClipRandom(m_puppet.lightDamageSoundNames, m_puppet.Pos);
                break;
            case HurtType.HeavyHurt:
                AudioExtensions.PlayClipRandom(m_puppet.heavyDamageSoundNames, m_puppet.Pos);
                break;
            case HurtType.KnockDown:
                AudioExtensions.PlayClipRandom(m_puppet.pushbackDamageSoundNames, m_puppet.Pos);
                break;
        }
    }

    public bool IsCanChangeState(int state) {
        if(state == (int)PuppetStateID.Death) {
            return true;
        }
        if(limitPlayTime > 0) {
            return false;
        }
        return true;
    }
}

public class PuppetClimbUpState : IFSMState {

    public string Name { get; set; }
    public int StateID { get; set; }
    Puppet m_puppet;
    float stateTime = 0f;       //状态时间结束就会退出状态
    float limitPlayTime = 0f;   //必须要播放完一定时间才可切换状态
    string climbUpAnimName;

    public PuppetClimbUpState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }
    public void OnEnter() {
        climbUpAnimName = m_puppet.GetClimbUpAnim();
        float animTime = m_puppet.m_animPlayer.GetAnimLength(climbUpAnimName);
        //提前结束，动画留一点时间用来做动画过渡。
        stateTime = animTime * Puppet.StateEndingTimeFactor;
        limitPlayTime = animTime * m_puppet.StateLimitTimeFactor;
        m_puppet.m_animPlayer.PlayAnimWithFade(climbUpAnimName, 0.05f);
    }

    public IEnumerator updateState() {
        while(m_puppet.StateID == PuppetStateID.ClimbUp) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            float dt = Time.deltaTime;
            if(stateTime < 0) {
                m_puppet.SetState(PuppetStateID.Stand);
                break;
            }
            stateTime -= dt;
            limitPlayTime -= dt;

            if(m_puppet.StateID == PuppetStateID.ClimbUp) {
                yield return null;
            }
        }
    }

    public bool IsCanChangeState(int state) {
        if(state == (int)PuppetStateID.Death) {
            return true;
        }
        if(limitPlayTime > 0) {
            return false;
        }
        return true;
    }
}


/// <summary>
/// 人物被控制的状态。即单纯的播动画。人物将不受玩家控制。
///
/// 如：击晕，变羊。
///
/// </summary>
public class PuppetControlState : IFSMState {

    public string Name { get; set; }
    public int StateID { get; set; }
    Puppet m_puppet;

    float stateTime = 0f;      //状态时间结束就会退出状态
    float limitPlayTime = 0f;  //至少要播放完一定时间才可切换状态
    string CurHurtAnimName;

    public PuppetControlState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }

    public void OnEnter() {
        ControlInfo controlInfo = m_puppet.m_controlInfo;
        CurHurtAnimName = controlInfo.m_controlAnim;
        float controlTime = controlInfo.m_controlTime;

        if(controlTime <= 0) {
            controlTime = m_puppet.m_animPlayer.GetAnimLength(CurHurtAnimName);
        }
        //提前结束，动画留一点时间用来做动画过渡。
        stateTime = controlTime;
        limitPlayTime = controlTime;
        m_puppet.PuppetLog("limitTime:[{0}]", limitPlayTime);
        m_puppet.m_animPlayer.PlayAnimWithBlenad(CurHurtAnimName);
        playAuido();
    }

    public IEnumerator updateState() {
        while(m_puppet.StateID == PuppetStateID.Control) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            float dt = Time.deltaTime;
            if(stateTime < 0) {
                m_puppet.SetState(PuppetStateID.Stand);
                break;
            }
            stateTime -= dt;
            limitPlayTime -= dt;

            if(m_puppet.StateID == PuppetStateID.Control) {
                yield return null;
            }
        }
    }

    void playAuido() {

    }

    public bool IsCanChangeState(int state) {
        if(state == (int)PuppetStateID.Death) {
            return true;
        }
        if(limitPlayTime > 0) {
            return false;
        }
        return true;
    }
}

public class PuppetWinState : IFSMState {

    public string Name { get; set; }
    public int StateID { get; set; }
    Puppet m_puppet;
    float stateTime = 0f;

    public PuppetWinState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }
    public void OnEnter() {
        float animTime = m_puppet.m_animPlayer.GetAnimLength(m_puppet.winAnimName);
        //提前结束，动画留一点时间用来做动画过渡。
        stateTime = animTime * Puppet.StateEndingTimeFactor;
        m_puppet.m_animPlayer.PlayAnimWithFade(m_puppet.winAnimName, 0.1f);
    }
    public IEnumerator updateState() {
        while(m_puppet.StateID == PuppetStateID.Win) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            float dt = Time.deltaTime;
            if(stateTime < 0) {
                m_puppet.SetState(PuppetStateID.Stand);
                break;
            }
            stateTime -= dt;

            if(m_puppet.StateID == PuppetStateID.Win) {
                yield return null;
            }
        }
    }

    public bool IsCanChangeState(int state) {
        return true;
    }
}

public class PuppetReloadState : IFSMState {

    public string Name { get; set; }
    public int StateID { get; set; }
    Puppet m_puppet;
    float stateTime = 0f;       //状态时间结束就会退出状态
    float limitPlayTime = 0f;   //必须要播放完一定时间才可切换状态

    float soundTime = 0;

    public PuppetReloadState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }
    public void OnEnter() {

        string animName = m_puppet.reloadAnimName;
        float animTime = m_puppet.m_animPlayer.GetAnimLength(animName);
        float reloadTime = m_puppet.reloadTime;

        if(reloadTime > 0) {
            m_puppet.m_animPlayer.SetSpeed(animName, animTime / reloadTime);
            animTime = reloadTime;
        } else {
            m_puppet.m_animPlayer.SetSpeed(animName, 1.0f);
        }

        soundTime = animTime * m_puppet.reloadSoundPlayTime;
        limitPlayTime = animTime * m_puppet.StateLimitTimeFactor;
        //提前结束，动画留一点时间用来做动画过渡。
        stateTime = animTime * Puppet.StateEndingTimeFactor;
        m_puppet.m_animPlayer.PlayAnimWithFade(animName, 0.1f);
    }
    public IEnumerator updateState() {
        while(m_puppet.StateID == PuppetStateID.Reload) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            float dt = Time.deltaTime;
            if(stateTime < 0) {
                m_puppet.FillCurGunSkill();
                m_puppet.SetState(PuppetStateID.Stand);
                break;
            }
            stateTime -= dt;
            limitPlayTime -= dt;

            if(soundTime < 0) {
                AudioExtensions.PlayClip(m_puppet.reloadSoundName, m_puppet.Pos);
            }

            if(m_puppet.StateID == PuppetStateID.Reload) {
                yield return null;
            }
        }
    }

    public bool IsCanChangeState(int state) {
        if(state == (int)PuppetStateID.Death) {
            return true;
        }
        if(limitPlayTime > 0) {
            return false;
        }
        return true;
    }
}

public class PuppetDeathState : IFSMState {

    public string Name { get; set; }
    public int StateID { get; set; }
    Puppet m_puppet;
    float stateTime;

    public PuppetDeathState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }

    public void OnEnter() {
        float animTime = m_puppet.m_animPlayer.GetAnimLength(m_puppet.deadAnimName);
        //提前结束，动画留一点时间用来做动画过渡。
        stateTime = animTime;
        m_puppet.m_animPlayer.PlayAnimWithFade(m_puppet.deadAnimName);
        playHurtAuido();
    }

    public IEnumerator updateState() {

        while(m_puppet.StateID == PuppetStateID.Death) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            float dt = Time.deltaTime;
            //1.检测死亡动画完成后，删除对象
            if(stateTime < 0) {
                m_puppet.DeadRelease();
                break;
            } else {
                yield return null;
            }
            stateTime -= dt;
        }
    }

    void playHurtAuido() {
        AudioExtensions.PlayClipRandom(m_puppet.deathSoundNames, m_puppet.Pos);

    }

    public bool IsCanChangeState(int state) {
        return true;
    }
}

public class PuppetSkillState : IFSMState {

    public string Name { get; set; }
    public int StateID { get; set; }
    Puppet m_puppet;

    public PuppetSkillState(Puppet puppet, string name, int stateID) {
        Name = name;
        StateID = stateID;
        m_puppet = puppet;
    }
    public void OnEnter() {
    }
    public IEnumerator updateState() {
        while(m_puppet.StateID == PuppetStateID.Skill) {
            if(m_puppet.IsTimeFreezing()) {
                yield return null;
                continue;
            }

            float dt = Time.deltaTime;

            //等待SkillInfo设置，释放技能脚本时会人物先进入技能状态，而后等待脚本中触发动画。
            SkillAnimInfo skillInfo = getAvailableSkillInfo();
            if(skillInfo == null) {
                if(m_puppet.StateID == PuppetStateID.Skill) {
                    //0.检错判断
                    if(!m_puppet.m_skillsystem.IsCasting()) {
                        m_puppet.PuppetLogError("检测到错误。技能已经不再施放了，但是还是在skill状态。");
                    }
                    yield return null;
                }
                continue;
            }

            m_puppet.PuppetLog("[PuppetState] play skillInfo. {0}", skillInfo.Dump());

            //播放技能
            loadSkillInfo(skillInfo);

            bool isSkillInfoEnd = false;
            while(isSkillInfoEnd == false && m_puppet.StateID == PuppetStateID.Skill) {
                //bool fadeToNextAnim = false;
                //立刻转换动画
                if(m_puppet.NextSkillInfo != null) {
                    isSkillInfoEnd = true;
                    continue;
                }
                skillInfo.t_animTime += dt;
                if(skillInfo.isLoop == false) {
                    //正常结束
                    if(skillInfo.t_animTime >= skillInfo.animTime) {
                        //skillInfo结束不会直接设置站立。因为可能会有连续的skillInfo，只有整个技能脚本结束时，会设置站立状态。
                        //m_puppet.SetState(PuppetStateID.Stand);
                        isSkillInfoEnd = true;
                    }
//                     else if(fadeToNextAnim == false && skillInfo.t_animTime + m_puppet.FadeToNextAnimTime > skillInfo.animTime) {
//                         fadeToNextAnim = true;
//                     }

                } else {
                    //循环状态
                    if(skillInfo.t_animTime >= skillInfo.animTime) {
                        skillInfo.t_animTime = 0;
                    }
                }

                updateLookDir(skillInfo, dt);
                updateMove();
                //再次检测循环条件，如果要退出循环的话，就快点退出。
                if(isSkillInfoEnd == false && m_puppet.StateID == PuppetStateID.Skill) {
                    yield return null;
                }
            }
            m_puppet.SkillInfo = null;
        }
    }

    SkillAnimInfo getAvailableSkillInfo() {
        SkillAnimInfo skillInfo = m_puppet.SkillInfo;
        if(skillInfo == null && m_puppet.NextSkillInfo != null) {
            skillInfo = m_puppet.NextSkillInfo;
            m_puppet.SkillInfo = skillInfo;
            m_puppet.NextSkillInfo = null;
        }
        if(skillInfo != null) {
            skillInfo.Reset();
        }

        return skillInfo;
    }

    void loadSkillInfo(SkillAnimInfo skillInfo) {
        if(skillInfo == null) return;
        AnimPlayer animPlayer = m_puppet.m_animPlayer;

        string animName = skillInfo.animation;
        if(skillInfo.animSyn) {
            animPlayer.SetSpeed(animName, animPlayer.GetAnimLength(animName) / skillInfo.animTime);
        } else {
            animPlayer.SetSpeed(animName, 1.0f);
        }
        if(skillInfo.isLoop) {
            animPlayer.SetWrapMode(animName, WrapMode.Loop);
        }
        animPlayer.PlayAnimWithFade(animName, 0.05f, PlayMode.StopAll);
    }

    void updateLookDir(SkillAnimInfo skillInfo, float dt) {
        switch(skillInfo.lookSyn) {
            case LookSkillSyn.MoveSyn:
                m_puppet.LookAtDir(m_puppet.CurMoveDir);
                break;
            case LookSkillSyn.LookSyn:
                m_puppet.LookAtDir(m_puppet.TargetLookingDir);
                break;
        }
    }

    void updateMove() {
        float dt = Time.deltaTime;
        //m_puppet.PuppetSkillMoveInfo.moveSpeed = m_puppet.MoveSpeed;
        Vector3 nextPos = m_puppet.objTF.position;
        //强制位移
        //Tools.ObjForceStep(dt, m_puppet.objTF.position, m_puppet.PuppetForceMoveInfos.Values, out nextPos);
        //技能位移
        if(m_puppet.PuppetSkillMoveInfo != null && m_puppet.PuppetSkillMoveInfo.isEnable) {
            bool isMoveEnd =  m_puppet.PuppetSkillMoveInfo.ObjMoveStep(dt, nextPos, out nextPos);
            //m_puppet.CurMoveDir = nextPos - m_puppet.transform.position;
            //m_puppet.objTF.position = nextPos;
            m_puppet.SimpleMove(nextPos);
            if(isMoveEnd) {
                //移动结束
                m_puppet.ClearSkillMove();
            }
        }
    }

    public bool IsCanChangeState(int state) {
        return true;
    }

}



}


